from PySide6.QtCore import QIODevice, QObject, QTimer, Slot, Signal, Property, QEnum
from PySide6.QtQml import QmlElement, QmlSingleton
from PySide6.QtSerialPort import QSerialPortInfo, QSerialPort
import logging
import re
from enum import Enum

    
# 定义 QML 模块名称和版本
QML_IMPORT_NAME = "RaPair"
QML_IMPORT_MAJOR_VERSION = 1

@QmlElement
class RaPair(QObject):
    # 串口状态改变信号
    serialPortStateChanged = Signal(int)
    
    dataReceived = Signal(str)
    
    loraIdReceived = Signal(str)
    regionReceived = Signal(str)
    
    def __init__(self, parent=None):
        super().__init__(parent)
        self._serialPort = QSerialPort()
        self._serialPort.readyRead.connect(self._readData)
        self._serialPort.errorOccurred.connect(self._handleError)
        
        self._serialPortState = self.SerialPortState.Closed
        
        self._dataBuffer = ''
        self._read_loara_id = False
        self._read_region = False
       
    # 串口状态枚举
    @QEnum
    class SerialPortState(Enum):
        Closed, Opened = range(2)
    
    # 串口状态
    @Property(int, notify=serialPortStateChanged)
    def portState(self):
        return self._serialPortState.value

    # 打开串口
    @Slot(str)
    def openSerialPort(self, portName):
        self._serialPort.setPortName(portName)
        self._serialPort.setBaudRate(QSerialPort.BaudRate.Baud115200)
        if self._serialPort.open(QIODevice.ReadWrite):
            logging.info(f"Serial port {portName} opened successfully.")
            self._serialPortState = self.SerialPortState.Opened
            logging.info(f"Serial port state: {self._serialPortState.value}")
            self.serialPortStateChanged.emit(self._serialPortState.value)
            self._serialPort.write("\r\n".encode())  # 发送空命令
        else:
            logging.error(f"Failed to open serial port {portName}.")
            self._serialPortState = self.SerialPortState.Closed
            self.serialPortStateChanged.emit(self._serialPortState.value)

    # 关闭串口
    @Slot()
    def closeSerialPort(self):
        self._serialPort.close()
        logging.info("Serial port closed.")
        self._serialPortState = self.SerialPortState.Closed
        self.serialPortStateChanged.emit(self._serialPortState.value)
        
    @Slot(str)
    def setLoraID(self, lora_id):
        if self._serialPortState == self.SerialPortState.Opened:
            # 发送设置配对码命令
            command = f"lora_set_pair_id {lora_id}\r\n".encode()
            self._serialPort.write(command)
            logging.info(f"Sent command to set LoRa ID: {lora_id}")
        else:
            logging.warning("Cannot set LoRa ID. Serial port is not opened.")
    @Slot(str)
    def setRegion(self, region):
        if self._serialPortState == self.SerialPortState.Opened:
            # 发送设置区域命令
            command = f"lora_set_region {region}\r\n".encode()
            self._serialPort.write(command)
            logging.info(f"Sent command to set region: {region}")
        else:
            logging.warning("Cannot set region. Serial port is not opened.")
    
    @Slot()
    def getLoraID(self):
        if self._serialPortState == self.SerialPortState.Opened:
            command = "lora_get_pair_id\r\n".encode()
            self._serialPort.write(command)
            logging.info("Sent command to get LoRa ID")
        else:
            logging.warning("Cannot get LoRa ID. Serial port is not opened.")
    

    @Slot()
    def getRegion(self):
        if self._serialPortState == self.SerialPortState.Opened:
            command = "lora_get_region\r\n".encode()
            self._serialPort.write(command)
            logging.info("Sent command to get region")
        else:
            logging.warning("Cannot get region. Serial port is not opened.")
    
    def _handleError(self, error):
        if error != QSerialPort.NoError:
            if self._serialPortState != self.SerialPortState.Closed:
                self._serialPortState = self.SerialPortState.Closed
                logging.error(f"Serial port error: {error}, closing port.")
                self.serialPortStateChanged.emit(self._serialPortState.value)
            else:
                logging.error(f"Serial port error: {error}")
            self._serialPort.clearError()
            self._serialPort.close()
    
    def _readData(self):
        self._dataBuffer += self._serialPort.readAll()
        while '\n' in self._dataBuffer:
            line, self._dataBuffer = self._dataBuffer.split('\n', 1)
            if 'lora_get_pair_id' in line:
                self._read_loara_id = True
            elif 'lora_get_region' in line:
                self._read_region = True
            if self._read_loara_id:
                result = re.search(r'(0x[0-9a-fA-F]*)', line)
                if result:
                    loraId = result.group(1)
                    displayId = f"{self.hexToDec(loraId)} ({loraId})"
                    self.loraIdReceived.emit(displayId)
                    self._read_loara_id = False
                    logging.info(f"Lora ID received: {loraId}")
            elif self._read_region:
                result = re.search(r'region: (\d+)', line)
                if result:
                    self.regionReceived.emit(result.group(1))
                    self._read_region = False
                    logging.info(f"Region received: {result.group(1)}")
            self.dataReceived.emit(line.strip())
    
    def hexToDec(self, hex_str):
        try:
            return str(int(hex_str, 16))
        except ValueError:
            logging.error(f"Invalid hex string: {hex_str}")
            return "0"